use anyhow::{ anyhow, Result };
use eastmoney::kzz::kzz_rpt_bond_cb_list;
use eastmoney::kzz::kzz_rpt_bond_cb_list::RptBondCbList;
use eastmoney::kzz::kzz_rpt_bond_cb_list::RptBondCbListResult;
use fudata::db;
use fudata::model::security;
use fudata::model::security::Market;
use fudata::model::FudaRecord;
use log::debug;
use log::error;
use log::info;
use log::warn;
use serde_json::Value;

/// 可转债清单
///
/// pn: page-number start from 1,
///
/// pz: page-size e.g. 20
///
pub async fn update_kzz_list_page_from_eastmoney(pn: u16, pz: u16) -> Result<Vec<FudaRecord>> {
    let mut updates = Vec::new();
    let rpt = kzz_rpt_bond_cb_list
        ::rpt_bond_cb_list(pn, pz).await
        .map_err(|err| anyhow!(err.to_string()))?;

    if let RptBondCbList { result: Some(RptBondCbListResult { data, .. }), .. } = rpt {
        for (index, kzz) in data.into_iter().enumerate() {
            if
                let (
                    Some(Value::String(security_code)),
                    Some(Value::String(secu_code)),
                    Some(Value::String(security_name_abbr)),
                    Some(Value::String(_convert_stock_code)),
                    delist_date,
                ) = (
                    &kzz.security_code,
                    &kzz.secu_code,
                    &kzz.security_name_abbr,
                    &kzz.convert_stock_code,
                    &kzz.delist_date,
                )
            {
                let mkt = if secu_code.ends_with(&Market::SH.to_string()) {
                    Market::SH
                } else if secu_code.ends_with(&Market::SZ.to_string()) {
                    Market::SZ
                } else if secu_code.ends_with(&Market::BJ.to_string()) {
                    Market::BJ
                } else {
                    warn!(
                        "Ignore unknown security::Market for secu_code: {}({})",
                        secu_code,
                        security_name_abbr
                    );
                    continue;
                    // "NQ".to_string()
                };
                if delist_date.is_some() {
                    debug!(
                        "Delete & Ignore delisted kzz[{index}]: {:?} {:?} {:?} {:?} delist_date={:?}",
                        kzz.security_code,
                        kzz.secu_code,
                        kzz.security_name_abbr,
                        kzz.convert_stock_code,
                        kzz.delist_date
                    );
                    let security_id = security::Security::id_from(
                        &mkt,
                        &security::SecurityType::KZZ,
                        security_code
                    )?;
                    let _ = db::security::delete(&security_id).await?;
                } else {
                    let security = security::Security {
                        market: mkt,
                        type_: security::SecurityType::KZZ,
                        symbol: security_code.to_string(),
                        name: Some(security_name_abbr.to_string()),
                        ..Default::default()
                    };

                    // Update a security record with a specific id
                    let updated: Option<FudaRecord> = db::security::upsert(&security).await?;

                    if let Some(u) = updated {
                        updates.push(u);
                    }
                }
            }
        }
    }
    info!(
        "(kzzs_craw::update_kzz_list_page_from_eastmoney) success! for page: {pn} page-size: {pz}"
    );
    Ok(updates)
}

pub async fn update_kzz_list_all_from_eastmoney() -> Result<()> {
    let mut pn = 1;
    let pz = 1000;

    loop {
        info!(
            "(kzzs_craw::update_kzz_list_all_from_eastmoney) start! for page: {pn} page-size: {pz}"
        );
        let rs = update_kzz_list_page_from_eastmoney(pn, pz).await;
        match rs {
            Ok(records) => {
                if records.is_empty() {
                    info!("debug No more records available; exit the loop");

                    break; // No more records available; exit the loop
                }
                pn += 1; // Increment the page number for the next request
                debug!("..{}", pn);
            }
            Err(error) => {
                // 错误处理: 打印错误日志并采取相应措施
                error!("Update error on page {}: {:?}", pn, error);

                return Err(error); // An error occurred; exit the loop
            }
        }
        info!(
            "(kzzs_craw::update_kzz_list_all_from_eastmoney) /end! for page: {pn} page-size: {pz}"
        );
    }

    Ok(())
}

#[cfg(test)]
mod test {
    use log::debug;
    use serde::Deserialize;

    use crate::kzz::kzzs_craw::update_kzz_list_page_from_eastmoney;

    #[derive(Debug, Deserialize, PartialEq)]
    struct Count {
        count: u64,
    }

    #[tokio::test]
    async fn test_debug_print() {
        pretty_env_logger::init_timed();
        debug!("debug No more records available; exit the loop");
        println!("print No more records available; exit the loop");
    }

    #[tokio::test]
    async fn test_update_kzz_list_from_eastmoney() {
        pretty_env_logger::init_timed();
        let _ = fudata::db::init().await.unwrap();

        let pn = 1;
        let pz = 100;
        let rs = update_kzz_list_page_from_eastmoney(pn, pz).await;
        debug!("rs: {:?}", rs)
    }
}
